#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <functional>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#include "InetAddr.hpp"
#include <sys/types.h>
#include <sys/socket.h>
#include <memory>
#include <pthread.h>
#include "Socket.hpp"
using namespace std;
using namespace socket_ns;
using io_service_t = function<void(socket_sptr sockfd, InetAddr client)>;
class TcpServer;

// 线程数据包
class ThreadData
{
public:
    ThreadData(socket_sptr fd, InetAddr peer, TcpServer *self)
        : _fd(fd), _clientserver(peer), _self(self)
    {
    }
    socket_sptr _fd;        // 文件描述符
    InetAddr _clientserver; // 客户端网络信息
    TcpServer *_self;       // 指向TcpServer类的指针
};

class TcpServer
{
public:
    TcpServer(int port, io_service_t service)
        : _localaddr("0", port), _listensock(make_unique<TcpSocket>()), _isrunning(false), _service(service)
    {
        _listensock->BulidListenSocket(_localaddr);
    }
    ~TcpServer()
    {
    }

    // 线程执行函数
    // 由于线程执行函数只能拥有一个参数，类中的函数默认具有this指针，所以此处使用static修饰该函数
    static void *HandlerSock(void *args)
    {
        ThreadData *td = static_cast<ThreadData *>(args);
        // 将线程进行分离-主线程不用Join该线程
        pthread_detach(pthread_self());
        // 修饰之后需要使用该类中的Service函数，需要this指针
        // 让该线程执行服务
        td->_self->_service(td->_fd, td->_clientserver);
        close(td->_fd->SockFd());
        // 用完销毁数据包
        delete (td);
        return nullptr;
    }

    // 循环运行服务
    void Loop()
    {
        // 4.不能直接收取数据，首先得获取连接
        _isrunning = true;
        while (_isrunning)
        {
            // 获取连接，拿到通信客户端之后，
            // 会获得一个文件描述符，
            // 这个文件描述符才是真正为通信客户端提供服务的文件描述符，
            // 之前的套接字文件描述符只负责监听。

            // 4.1 获取连接
            // peer和len为输出型参数，将与服务器获取连接的用户端的信息输出出来
            InetAddr peeraddr;
            socket_sptr normalsock = _listensock->Accepter(&peeraddr);

            // 5.给成功连接的客户端提供服务
            // 版本3-使用多线程
            pthread_t t;                                                 // 创建线程
            ThreadData *td = new ThreadData(normalsock, peeraddr, this); // 线程数据包
            pthread_create(&t, nullptr, HandlerSock, td);                // 创建线程
        }
        _isrunning = false;
    }

private:
    InetAddr _localaddr;
    unique_ptr<Socket> _listensock; // 监听套接字
    io_service_t _service;
    bool _isrunning;
};